<?php

/*
 * This file is part of SwiftMailer. (c) 2004-2009 Chris Corbyn For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
 */

/**
 * Redudantly and rotationally uses several Transports when sending.
 *
 * @package Swift
 * @subpackage Transport
 * @author Chris Corbyn
 */
class Swift_Transport_LoadBalancedTransport implements Swift_Transport {
	/**
	 * Transports which are deemed useless
	 */
	private $_deadTransports = array ();
	
	/**
	 * The Transports which are used in rotation.
	 *
	 * @var array Swift_Transport
	 * @access protected
	 */
	protected $_transports = array ();
	
	/**
	 * Creates a new LoadBalancedTransport.
	 */
	public function __construct() {
	}
	
	/**
	 * Set $transports to delegate to.
	 *
	 * @param array $transports
	 *        	Swift_Transport
	 */
	public function setTransports(array $transports) {
		$this->_transports = $transports;
		$this->_deadTransports = array ();
	}
	
	/**
	 * Get $transports to delegate to.
	 *
	 * @return array Swift_Transport
	 */
	public function getTransports() {
		return array_merge ( $this->_transports, $this->_deadTransports );
	}
	
	/**
	 * Test if this Transport mechanism has started.
	 *
	 * @return boolean
	 */
	public function isStarted() {
		return count ( $this->_transports ) > 0;
	}
	
	/**
	 * Start this Transport mechanism.
	 */
	public function start() {
		$this->_transports = array_merge ( $this->_transports, $this->_deadTransports );
	}
	
	/**
	 * Stop this Transport mechanism.
	 */
	public function stop() {
		foreach ( $this->_transports as $transport ) {
			$transport->stop ();
		}
	}
	
	/**
	 * Send the given Message.
	 *
	 * Recipient/sender data will be retrieved from the Message API.
	 * The return value is the number of recipients who were accepted for delivery.
	 *
	 * @param Swift_Mime_Message $message        	
	 * @param
	 *        	string[] &$failedRecipients to collect failures by-reference
	 * @return int
	 */
	public function send(Swift_Mime_Message $message, &$failedRecipients = null) {
		$maxTransports = count ( $this->_transports );
		$sent = 0;
		
		for($i = 0; $i < $maxTransports && $transport = $this->_getNextTransport (); ++ $i) {
			try {
				if (! $transport->isStarted ()) {
					$transport->start ();
				}
				if ($sent = $transport->send ( $message, $failedRecipients )) {
					break;
				}
			} catch ( Swift_TransportException $e ) {
				$this->_killCurrentTransport ();
			}
		}
		
		if (count ( $this->_transports ) == 0) {
			throw new Swift_TransportException ( 'All Transports in LoadBalancedTransport failed, or no Transports available' );
		}
		
		return $sent;
	}
	
	/**
	 * Register a plugin.
	 *
	 * @param Swift_Events_EventListener $plugin        	
	 */
	public function registerPlugin(Swift_Events_EventListener $plugin) {
		foreach ( $this->_transports as $transport ) {
			$transport->registerPlugin ( $plugin );
		}
	}
	
	// -- Protected methods
	
	/**
	 * Rotates the transport list around and returns the first instance.
	 *
	 * @return Swift_Transport
	 * @access protected
	 */
	protected function _getNextTransport() {
		if ($next = array_shift ( $this->_transports )) {
			$this->_transports [] = $next;
		}
		
		return $next;
	}
	
	/**
	 * Tag the currently used (top of stack) transport as dead/useless.
	 *
	 * @access protected
	 */
	protected function _killCurrentTransport() {
		if ($transport = array_pop ( $this->_transports )) {
			try {
				$transport->stop ();
			} catch ( Exception $e ) {
			}
			$this->_deadTransports [] = $transport;
		}
	}
}
